﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////

using Glodon.Gdmp.DB;
using System.Collections.Generic;
using System;
using System.Linq;

namespace Glodon.Gdmp.SDK.Samples.IFCExport
{
    /// <summary>
    /// 导出工具类。
    /// </summary>
    public static class ExportUtils
    {
        /// <summary>
        /// 导出核心逻辑。
        /// </summary>
        /// <param name="exportPath">导出路径。</param>
        /// <returns>true表示导出成功，否则为false。</returns>
        public static bool ExportCore(string exportPath)
        {
            var app = Application.Get();
            var document = app.ActiveDocument;

            //1.创建IFC数据模型对象，指定为IFC4版本。
            var model = IFCBuildingModel.Create(IFCSchemaVersion.IFC4);

            //2.进行数据初始化。
            model.Initialize();
            IFCStandardHeaderSection header = IFCStandardHeaderSection.Create("GLabModel.ifc");
            header.Description = "GLab Ifc Export";
            header.Name = "GLabModel.ifc";
            header.Organization = "Glodon GDMP";
            header.PreprocessorVersion = "GDMP Version 2024";
            header.OriginatingSystem = "Glodon GDMP GLab";
            model.SetStandardHeader(header);

            //3.将模型中的墙、梁、板封装成IFCEntity对象。
            //3.1 获取图元类别。
            var wallCategoryUid = document.GetElement(new ElementId(18014123631576115)).Category.UId;
            var floorCategoryUid = document.GetElement(new ElementId(18014123631577125)).Category.UId;
            var beamCategoryUid = document.GetElement(new ElementId(18014123631576737)).Category.UId;

            //3.2 根据类别获取对应的图元。
            var walls = new ElementCollector(document).OfCategoryId(wallCategoryUid).OfClass(typeof(FamilyInstance)).ToElements();
            var floors = new ElementCollector(document).OfCategoryId(floorCategoryUid).OfClass(typeof(FamilyInstance)).ToElements();
            var beams = new ElementCollector(document).OfCategoryId(beamCategoryUid).OfClass(typeof(FamilyInstance)).ToElements();

            //3.3 转换参数及根据图元创建IFCEntity的异步操作。
            var parameterConverter = model.GetParameterConverter();
            List<IFCObject> ifcObjects = new List<IFCObject>();
            Action<Element, ExportCategory, string> action = (element, category, groupName) =>
            {
                var entity = CreateIFCEntity(model, element, category);
                List<IFCEntity> entities = new List<IFCEntity>() { entity };
                var parameters = element.GetParameters();
                parameterConverter.Convert(entities, groupName, parameters);
                ifcObjects.Add(entity);
            };

            //3.4 遍历图元进行异步操作。
            walls.OrderBy(t => t.Id.Int64Value).ToList().ForEach(wall => action(wall, ExportCategory.Wall, "WallGroup"));
            floors.OrderBy(t => t.Id.Int64Value).ToList().ForEach(floor => action(floor, ExportCategory.Slab, "SlabGroup"));
            beams.OrderBy(t => t.Id.Int64Value).ToList().ForEach(beam => action(beam, ExportCategory.Beam, "BeamGroup"));
            
            //4.将实体创建为IFC聚合对象。
            AddEntitesToBuildingStorey(model, IFCUtils.IFCOwnerHistory, ifcObjects);

            //5.导出并保存IFC文件。
            var result = model.Save(exportPath);
            return result;
        }

        /// <summary>
        /// 根据类别创建对应的IFC实体。
        /// </summary>
        /// <param name="model"></param>
        /// <param name="element"></param>
        /// <param name="category"></param>
        /// <returns></returns>
        private static IFCEntity CreateIFCEntity(IFCBuildingModel model, Element element, ExportCategory category)
        {
            IFCEntity ifcEntity;
            switch (category)
            {
                case ExportCategory.Wall:
                    ifcEntity = model.CreateEntity("IFCWALL");
                    break;
                case ExportCategory.Slab:
                    ifcEntity = model.CreateEntity("IFCSLAB");
                    break;
                case ExportCategory.Beam:
                    ifcEntity = model.CreateEntity("IFCBEAM");
                    break;
                default:
                    return null;
            }

            List<IFCObject> attributes = new List<IFCObject>();
            //添加唯一标识符。
            var uniqueId = IFCValueObject.CreateIfcGlobalUniqueId();
            attributes.Add(uniqueId);
            //添加所有者信息。
            var ownerHistory = model.CreateEntity("IFCOWNERHISTORY");
            attributes.Add(ownerHistory);
            //添加标签，可以是图元的名称。
            var name = IFCValueObject.Create(element.Name, "IFCLABEL");
            attributes.Add(name);
            //添加描述，必须是IFCText类型。
            var description = IFCValueObject.Create("CustomDescription", "IFCTEXT");
            attributes.Add(description);
            //添加自定义IFCLabel。
            var label = IFCValueObject.Create("CustomLabel", "IFCLABEL");
            attributes.Add(label);

            //创建IFCLOCALPLACEMENT以确定相对位置。
            var localPlacement = model.CreateEntity("IFCLOCALPLACEMENT");
            List<double> origin = new List<double>() { 0, 0, 0 };
            Vector3d normal = new Vector3d(0, 0, 1);
            Vector3d direction = new Vector3d(1, 0, 0);
            var placement = IFCUtils.CreateIfcAxis2Placement3D(origin, normal, direction, model);
            IFCObject nullObject = null;
            localPlacement.SetAttributes(new List<IFCObject>() { nullObject, placement });
            attributes.Add(localPlacement);

            //定义几何转换器选项。
            IFCGeometryConvertOption geomOptions = IFCGeometryConvertOption.Create(element.Document, IFCExportMode.AdvancedBrep);
            var modelViews = new ElementCollector(element.Document).OfClass(typeof(ModelView)).ToElements();
            var modelView = modelViews.FirstOrDefault((m) => m.Name.Equals("1F"));
            //几何的视图参照以1F为准。
            geomOptions.ModelViewId = modelView.Id;
            //同时导出纹理信息。
            geomOptions.ExportTextureMap = true;

            List<IFCObject> representationAttributes = new List<IFCObject>();
            var reLabel = IFCValueObject.Create("representationLabelName", "IFCLABEL");
            representationAttributes.Add(reLabel);
            IFCValueObject reText = IFCValueObject.Create("representationTextName", "IFCTEXT");
            representationAttributes.Add(reText);

            var shape = element.GetGeometry(null);
            var geomConverter = model.GetGeometryConverter();
            var geometryEntity = geomConverter.Convert(shape, geomOptions);

            List<IFCObject> ifcObjects = new List<IFCObject>() { geometryEntity };
            var ifcAggregation = IFCAggregation.Create(ifcObjects);
            representationAttributes.Add(ifcAggregation);
            //IFCPRODUCTDEFINITIONSHAPE是图元所有表示的容器。
            var representation = model.CreateEntity("IFCPRODUCTDEFINITIONSHAPE");
            representation.SetAttributes(representationAttributes);
            attributes.Add(representation);

            //IFCIDENTIFIER仅用于标识目标系统的可读字符串信息。
            var identifier = IFCValueObject.Create("", "IFCIDENTIFIER");
            attributes.Add(identifier);

            //IFCWALLTYPEENUM用于确定墙是哪种类型的，这里我们使用USERDEFINED，即用户自定义。
            var wallTypeEnum = IFCValueObject.Create("USERDEFINED", "IFCWALLTYPEENUM");
            attributes.Add(wallTypeEnum);

            ifcEntity.SetAttributes(attributes);
            return ifcEntity;
        }

        /// <summary>
        /// 将实体创建为IFC聚合对象。
        /// </summary>
        /// <param name="iFCBuildingModel"></param>
        /// <param name="ownerHistory"></param>
        /// <param name="ifcObjects"></param>
        private static void AddEntitesToBuildingStorey(IFCBuildingModel model, IFCEntity ownerHistory, List<IFCObject> ifcObjects)
        {
            //IfcRelContainedInSpatialStructure用于将图元分配给项目的特定空间结构。
            var relEntity = model.CreateEntity("IFCRELCONTAINEDINSPATIALSTRUCTURE");

            List<IFCObject> attributes = new List<IFCObject>();
            //添加唯一标识符。
            var uniqueId = IFCValueObject.CreateIfcGlobalUniqueId();
            attributes.Add(uniqueId);
            //添加所有者信息。
            attributes.Add(ownerHistory);

            //添加标签。
            var name = IFCValueObject.Create("CustomName", "IFCLABEL");
            attributes.Add(name);

            //添加描述。
            var description = IFCValueObject.Create("CustomDescription", "IFCTEXT");
            attributes.Add(description);

            //添加图元对应的IFCObject集合。
            var objects = IFCAggregation.Create(ifcObjects);
            attributes.Add(objects);
            attributes.Add(IFCUtils.BuildingStory);

            //设置属性。
            relEntity.SetAttributes(attributes);
        }
    }
}
